home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / infosrvr / dev / scott / WWW / NextStep / Implementation / old / Anchor.m < prev    next >
Text File  |  1992-11-25  |  9KB  |  381 lines

  1. /*    Hypertext "Anchor" Object                Anchor.m
  2. **    ==========================
  3. **
  4. **    An anchor represents a region of a hypertext node which is linked to
  5. **    another anchor in the same or a different node.
  6. */
  7.  
  8. #define ANCHOR_CURRENT_VERSION 0
  9.  
  10. #import <ctype.h>
  11. #import <objc/Object.h>
  12. #import <objc/typedstream.h>
  13. #import <appkit/appkit.h>
  14. #import "Anchor.h"
  15. #import "HTUtils.h"
  16. #import "HTParse.h"
  17. #import "HyperText.h"
  18. #import "HyperManager.h"
  19.  
  20. @implementation Anchor:Object
  21.  
  22. static HyperManager *manager;
  23. static List * orphans;        // Grand list of all anchors with no parents
  24. List * HTHistory;        // List of visited anchors
  25.  
  26. + initialize 
  27. {
  28.     orphans = [List new];
  29.     HTHistory = [List new];
  30.     [Anchor setVersion:ANCHOR_CURRENT_VERSION];
  31.     return self;
  32. }
  33.  
  34. + setManager:aManager
  35. {
  36.     manager = aManager;
  37.     return self;
  38. }
  39. //                Creation Methods
  40. //                ================
  41. //
  42. //    Do not use "new" by itself outside this module. In order to enforce
  43. //    consistency, we insist that you furnish more information about the
  44. //    anchor you are creating.
  45. //
  46.  
  47. + new
  48. {
  49.     Anchor * new_anchor;
  50.     new_anchor = [super new];
  51.     new_anchor->DestAnchor = nil;
  52.     new_anchor->Address = (char *)0;
  53.     new_anchor->Sources = [List new];
  54.     new_anchor->children = [List new];
  55.     new_anchor->parent = 0;
  56.     return new_anchor;
  57. }
  58.  
  59.  
  60. //    Case insensitive string comparison
  61. //    ----------------------------------
  62. // On entry,
  63. //    s    Points to one string, null terminated
  64. //    t    points to the other.
  65. // On exit,
  66. //    returns    YES if the strings are equivalent ignoring case
  67. //        NO if they differ in more than  their case.
  68. //
  69. PRIVATE BOOL equivalent(const char * s, const char *t)
  70. {
  71.     for(;*s && *t; s++, t++) {
  72.         if (toupper(*s) != toupper(*t)) return NO;
  73.     }
  74.     return toupper(*s)==toupper(*t);
  75. }
  76.  
  77.  
  78. //    Create new or find old sub-anchor
  79. //    ---------------------------------
  80. //
  81. //    This one is for a new anchor being edited into an existing
  82. //    document. The parent anchor must already exist.
  83.  
  84. + newParent:(Anchor *)anAnchor tag:(const char *)tag
  85. {
  86.     List * kids = anAnchor->children;
  87.     int n = [kids count];
  88.     int i;
  89.     
  90.     for(i=0; i<n; i++) {
  91.     self = [kids objectAt:i];
  92.     if (equivalent(Address, tag)) {
  93.         if (TRACE) printf("Sub-anchor %p with name `%s' already exists.\n",
  94.         self, tag);
  95.         return self;
  96.     }
  97.     }
  98.  
  99.     self = [Anchor new];
  100.     if (TRACE) printf("new Anchor %p named `%s' is child of %p\n",
  101.                 self, tag, anAnchor);
  102.     parent = anAnchor;
  103.     [parent->children addObject:self];
  104.     StrAllocCopy(Address, tag);
  105.     return self;
  106. }
  107.  
  108.  
  109. //    Create new or find old named anchor
  110. //    -----------------------------------
  111. //
  112. //    This one is for a reference which is found in a document, and might
  113. //    not be already loaded.
  114. //    Note: You are not guarranteed a new anchor -- you might get an old one,
  115. //    like with fonts.
  116.  
  117. + newAddress:(const char *)anAddress;
  118. {
  119.     char * anc = HTParse(anAddress, "", PARSE_ANCHOR); // Anchor id specified?
  120.  
  121. //    If the node is a sub-anchor, we recursively load its parent.
  122. //    Then we create a sub-anchor within than node.
  123.  
  124.     if (*anc) {
  125.         char *nod = HTParse(anAddress, "",
  126.          PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
  127.         Anchor * foundParent = [Anchor newAddress:nod];
  128.     free(nod);
  129.     self = [Anchor newParent:foundParent tag:anc];
  130.         free (anc);
  131.  
  132. //    If the node has no parent, we check in a list of such nodes to see
  133. //    whether we have it.
  134.     
  135.     } else { /* Is not a sub anchor */
  136.     int i;
  137.     int n=[orphans count];
  138.     free(anc);
  139.     for(i=0; i<n; i++) {
  140.         self = [orphans objectAt:i];
  141.         if (equivalent(Address, anAddress)) {
  142.         if (TRACE) printf(
  143.             "Anchor %p with address `%s' already exists.\n",
  144.                 self, anAddress);
  145.         return self;
  146.         }
  147.     }
  148.         self = [Anchor new];
  149.     if (TRACE) printf("new Anchor %p has address `%s'\n", self, anAddress);
  150.         StrAllocCopy(Address, anAddress);
  151.         [orphans addObject:self];
  152.     }
  153.     return self;
  154. }
  155.  
  156.  
  157. //                Navigation    (Class methods)
  158. //                ==========
  159. //
  160. //        Go back in history
  161. //        ------------------
  162. +back
  163. {
  164.     return [[HTHistory removeLastObject] select];    // nil if no history
  165. }
  166.  
  167. //        Go to next logical step
  168. //        -----------------------
  169. //
  170. //    We take the link After or before the one we took to get where we are
  171. //
  172. +moveBy:(int)offset
  173. {
  174.     Anchor * up = [HTHistory lastObject];
  175.     if (up)
  176.     if (up->parent){
  177.         List * kids = up->parent->children;
  178.         unsigned i = [kids indexOf:up]; 
  179.     Anchor * nextOne =[kids objectAt:i+offset];
  180.     if (nextOne) {
  181.         [HTHistory removeLastObject];
  182.         [nextOne follow];
  183.     } else {
  184.         if (TRACE) printf("Anchor: No such logical step\n");
  185.     }
  186.     }
  187.     return self;
  188. }
  189.  
  190. +next        { return [self moveBy:+1]; }
  191. +previous    { return [self moveBy:-1]; }
  192.  
  193.  
  194. //        Reorder the children
  195. //        --------------------
  196. //
  197. //    This is necessary to ensure that an anchor which might have existed already
  198. //    in fact is put in the correct order as we load the node.
  199. //
  200. - isLastChild
  201. {
  202.     if(parent) {
  203.         List * siblings = parent->children;
  204.     [siblings removeObject:self];
  205.     return [siblings addObject:self];
  206.     }
  207.     return nil;
  208. }
  209.  
  210.  
  211. //
  212. //        Free an anchor
  213. //        --------------
  214. - free
  215. {
  216.     if (Address) free(Address);
  217.     if (parent) [parent->children removeObject:self];
  218.     if (TRACE) printf("Anchor: free called!  Not removed from Node!!!!!!!\n");
  219.     [Sources makeObjectsPerform:@selector(unload)];
  220.     if (!parent) [orphans removeObject:self];
  221.     return [super free];
  222. }
  223.  
  224. //    Get list of sources
  225.  
  226. - sources    { return Sources; }
  227.  
  228. //    Return parent
  229.  
  230. - parent    { return parent; }
  231.  
  232. //    Remove the reference from this anchor to an other
  233. //
  234. - unlink
  235. {
  236.     if (DestAnchor) {
  237.         (void) [(HyperText*)Node disconnectAnchor:self];    /* select */
  238.     [[DestAnchor sources] removeObject:self];
  239.     DestAnchor = nil;
  240.     }
  241.     return self;
  242. }
  243.  
  244. //    For allowing dangling links, when things disappear
  245.  
  246. - unload
  247. {
  248.     DestAnchor = nil;        /* invalidate the pointer */
  249.     return self;
  250. }
  251.  
  252. //    This removes the anchor from the structure entirely, and frees it.
  253. //
  254. - delete
  255. {
  256.     if (DestAnchor) [self unlink];            // Remove outgoing link
  257.     [Sources makeObjectsPerform:@selector(unlink)]; // Remove incomming links
  258.     return [self free];
  259. }
  260.  
  261. //    Set the region represented by the anchor
  262. //
  263. - (void) setNode: (id) node
  264. {
  265.     Node = node;
  266. }
  267.  
  268.  
  269.  
  270.  
  271. /*    Select the anchor                        select
  272. **    -----------------
  273. **
  274. **    This will load the node is necessary, if the anchor has only a network
  275. **    address.
  276. */
  277. - selectDiagnostic: (int) diag
  278. {
  279.     Anchor * nodeAnchor = parent ? parent : self;
  280.  
  281.     if (!nodeAnchor->Node) {        /* If the node is not loaded, */
  282.     if (!nodeAnchor->Address) {
  283.         if (TRACE) printf(
  284.             "Anchor %p: node not loaded, no address!\n", nodeAnchor);
  285.         return nil;
  286.     } else {
  287.         if (![manager loadAnchor:nodeAnchor Diagnostic:diag]) {
  288.         if (TRACE) printf("Anchor %p: Couldn't load node `%s'!\n",
  289.              nodeAnchor, nodeAnchor->Address);
  290.         return nil;
  291.         }
  292.     }
  293.     }
  294.     if (!nodeAnchor) return nil;            /* Failed */
  295.     if (!nodeAnchor->Node) return nodeAnchor;        /* Ok, foreign */
  296.     return [nodeAnchor->Node selectAnchor:self];    /* Ok, text */    
  297. }
  298.  
  299.  
  300. /*    Select the anchor                        select
  301. **    -----------------
  302. **
  303. **    This will load the node is necessary, if the anchor has only a network
  304. **    address.
  305. */
  306. - select
  307. {
  308.     return [self selectDiagnostic:0];
  309. }
  310.  
  311.  
  312. //    Set reference string
  313. - setAddress: (const char *) ref_string
  314. {
  315.    if (TRACE) printf("Anchor %p has address `%s'\n", self, ref_string);
  316.    StrAllocCopy(Address, ref_string);
  317.    return self;
  318. }
  319.  
  320. //    Return the address of this anchor
  321.  
  322. - (const char *)address
  323. {
  324.     return Address;
  325. }
  326.  
  327. //    Generate a malloc'd string for the FULL anchor address
  328. //
  329. - (char *)fullAddress
  330. {
  331.     char * result;
  332.     if (parent) {
  333.         result = (char *) malloc(
  334.         strlen(Address) + 1 + strlen([parent address])+ 1);
  335.     strcpy(result, [parent address]);
  336.     strcat(result, "#");
  337.     strcat(result, Address);
  338.     } else { /* no parent */
  339.         result = (char *) malloc(strlen(Address)+ 1);
  340.     strcpy(result, Address);
  341.     }
  342.     return result;
  343. }
  344.  
  345. //    Link this Anchor to another given one
  346. //    -------------------------------------
  347.  
  348. - (void) linkTo:(Anchor *)destination;
  349. {
  350.     if (TRACE) printf(
  351.         "Anchor: Linking anchor %p to anchor %p\n", self, destination);
  352.     DestAnchor = destination;
  353.     [destination->Sources addObject:self];
  354. }
  355.  
  356. //    Follow a link to its destination
  357. //    --------------------------------
  358. - (BOOL) follow;
  359. {
  360.     if (DestAnchor)
  361.     if ([DestAnchor select]) {
  362.         if (TRACE) printf("Anchor: followed link from %p to %p\n",
  363.                                 self, DestAnchor);
  364.         [HTHistory addObject:self];
  365.         return YES;
  366.     }
  367.     
  368.     return NO;
  369. }
  370.  
  371. //    Figure out the node from that of the parent
  372.  
  373. - node
  374. {
  375.     return parent ? [parent node] : Node;
  376. }
  377.  
  378. - destination { return DestAnchor; }
  379.  
  380. @end
  381.